#ifndef _FTPCLIENTSESSION_HPP_
#define _FTPCLIENTSESSION_HPP_

#include "jhdefs.hpp"
#include "types.hpp"
#include "net/DialogSocket.hpp"
#include "net/StreamSocket.hpp"
//#include "net/SocketStream.hpp"
#include "net/SocketAddress.hpp"
#include "stdlib/timespan.hpp"
using JHCPP::stdlib::CTimeSpan;
#include <string>

NET_NAMESPACE_BEGIN

class CSocketStream;
class CFTPClientSession
{
public:
	enum
	{
		FTP_PORT = 21
	};

	enum FileType
	{
		TYPE_TEXT,   // TYPE A (ASCII)
		TYPE_BINARY  // TYPE I (Image)
	};

	CFTPClientSession();
	explicit CFTPClientSession(const CStreamSocket& socket);
	CFTPClientSession(const std::string& host,
		UInt16 port = FTP_PORT,
		const std::string& username = "",
		const std::string& password = "");
	virtual ~CFTPClientSession();

	void setTimeout(const CTimeSpan& timeout);
	CTimeSpan getTimeout() const;

	void setPassive(bool flag, bool useRFC1738 = true);
	bool getPassive() const;

	void open(const std::string& host,
		UInt16 port,
		const std::string& username = "",
		const std::string& password = "");
	void login(const std::string& username, const std::string& password);
	void logout();
	void close();

	std::string systemType();
	void setFileType(FileType type);
	FileType getFileType() const;

	void setWorkingDirectory(const std::string& path);
	std::string getWorkingDirectory();

	void cdup();

	void rename(const std::string& oldName, const std::string& newName);
	void remove(const std::string& path);

	void createDirectory(const std::string& path);
	void removeDirectory(const std::string& path);

	std::istream& beginDownload(const std::string& path);
	void endDownload();
	std::ostream& beginUpload(const std::string& path);
	void endUpload();
	std::istream& beginList(const std::string& path = "", bool extended = false);
	void endList();
	void abort();

	int sendCommand(const std::string& command, std::string& response);
	int sendCommand(const std::string& command, const std::string& arg, std::string& response);

	bool isOpen() const;
	bool isLoggedIn() const;

protected:
	enum StatusClass
	{
		FTP_POSITIVE_PRELIMINARY  = 1,
		FTP_POSITIVE_COMPLETION   = 2,
		FTP_POSITIVE_INTERMEDIATE = 3,
		FTP_TRANSIENT_NEGATIVE    = 4,
		FTP_PERMANENT_NEGATIVE    = 5
	};
	enum
	{
		DEFAULT_TIMEOUT = 30000000 // 30 seconds default timeout for socket operations	
	};

	static bool isPositivePreliminary(int status);
	static bool isPositiveCompletion(int status);
	static bool isPositiveIntermediate(int status);
	static bool isTransientNegative(int status);
	static bool isPermanentNegative(int status);
	std::string extractPath(const std::string& response);
	CStreamSocket establishDataConnection(const std::string& command, const std::string& arg);
	CStreamSocket activeDataConnection(const std::string& command, const std::string& arg);
	CStreamSocket passiveDataConnection(const std::string& command, const std::string& arg);
	void sendPortCommand(const CSocketAddress& addr);
	CSocketAddress sendPassiveCommand();
	bool sendEPRT(const CSocketAddress& addr);
	void sendPORT(const CSocketAddress& addr);
	bool sendEPSV(CSocketAddress& addr);
	void sendPASV(CSocketAddress& addr);
	void parseAddress(const std::string& str, CSocketAddress& addr);
	void parseExtAddress(const std::string& str, CSocketAddress& addr);
	void endTransfer();

private:
	CFTPClientSession(const CFTPClientSession&);
	CFTPClientSession& operator = (const CFTPClientSession&);

	std::string		m_host;
	UInt16			m_port;
	CDialogSocket*	m_pControlSocket;
	CSocketStream*	m_pDataStream;
	bool			m_passiveMode;
	FileType		m_fileType;
	bool			m_supports1738;
	bool			m_serverReady;
	bool			m_isLoggedIn;
	CTimeSpan		m_timeout;
};


NET_NAMESPACE_END

#endif
